/**
 * Copyright (C) 2009 Noel Ang
 *
 * This file is part of Samsara.
 *
 * Samsara is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Samsara is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Samsara.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package com.lunarshades.samsara;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JTextArea;
import javax.swing.text.JTextComponent;

import com.lunarshades.samsara.logging.KarmaChangeLogger;
import com.lunarshades.samsara.logging.Loggable;
import com.lunarshades.samsara.logging.Logger;
import com.lunarshades.samsara.logging.SwingDocumentLogger;
import com.lunarshades.samsara.logging.WriterLogger;
import com.lunarshades.samsara.ui.actions.LoggingStartEvent;
import com.lunarshades.samsara.ui.actions.LoggingStopEvent;

/**
 * Logging management facility for this application.
 *
 * @author Noel Ang, sparta.variable@gmail.com
 */
public class Recorder {

public Recorder() {
    mIsStarted = false;
    mTarget = null;
    mLoggers = Collections.synchronizedList(new ArrayList<Logger>());
    createLoggers();
}

private void createLoggers() {

    if (mTarget instanceof File) {
        try {
            FileWriter writer = new FileWriter((File) mTarget);
            mLoggers.add(new WriterLogger(writer, false, false));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    } else {
        if (Debug.active()) {
            mLoggers.add(new WriterLogger(new OutputStreamWriter(System.err),
                    true,
                    false
            )
            );
        } else {
            mLoggers.add(new WriterLogger(new OutputStreamWriter(System.out),
                    true,
                    false
            )
            );
        }
    }

    // Karma delta tracker (audit trail output when logging is stopped)
    mLoggers.add(mKarmaChangeLogger = new KarmaChangeLogger());

    // Log console is special: it is created only once, and destroyed only on
    // Recorder death.
    synchronized (this) {
        if (mSwingDocumentLogger == null) {
            mSwingDocumentLogger = new SwingDocumentLogger();
        }
    }
    mLoggers.add(mSwingDocumentLogger);
}

private void destroyLoggers() {
    synchronized (mLoggers) {
        for (Logger logger : mLoggers) {
            logger.dispose();
        }
        mLoggers.clear();
    }
}

public void log(Loggable loggable) {
    if (isStarted()) {
        synchronized (mLoggers) {
            for (Logger logger : mLoggers) {
                logger.log(loggable);
            }
        }
    }
}

public boolean isStarted() {
    return mIsStarted;
}

public synchronized void start(File file) {
    mTarget = file;
    stop();
    start();
}

public synchronized void start() {
    if (mLoggers.isEmpty()) {
        createLoggers();
    }
    mIsStarted = true;
    log(new LoggingStartEvent());
}

public synchronized void stop() {
    Loggable[] karmaTrackLoggables = mKarmaChangeLogger.dumpLog();
    for (Loggable msg : karmaTrackLoggables) {
        log(msg);
    }
    log(new LoggingStopEvent());
    mIsStarted = false;
    destroyLoggers();
}

public synchronized JTextComponent getLoggingConsoleView() {
    if (mView == null) {
        mView = new JTextArea(mSwingDocumentLogger);
        mView.setLineWrap(true);
        mView.setWrapStyleWord(true);
        mView.setEditable(false);
        mView.setFocusable(false);
    }
    return mView;
}

private final List<Logger> mLoggers;
private volatile KarmaChangeLogger mKarmaChangeLogger;
private volatile SwingDocumentLogger mSwingDocumentLogger;
private volatile JTextArea mView;
private volatile boolean mIsStarted;
private volatile Object mTarget;
}
